---
title: "JavaScript 與 C 語法對比"
description: "從 JavaScript 開發者的角度，深入對比 JavaScript 和 C 語言的語法差異，幫助您快速掌握 C 語言的語法和核心概念。"
---

# JavaScript 與 C 語法對比

## 1. 引言

### 為什麼需要語法對比？

作為一名 JavaScript 開發者，轉向 C 語言需要理解兩者在語法和編程範式上的根本差異。通過系統的語法對比，我們可以：

- 快速理解 C 語言的語法規則和約束
- 避免常見的語法錯誤和陷阱
- 建立兩種語言之間的概念映射
- 為 C 語言編程打下堅實的基礎

> 💡 **學習策略**: 將 C 語言想像成一種「低級方言」，它讓你能夠直接控制記憶體和硬體，這與 JavaScript 的高級抽象完全不同。

## 2. 基礎語法對比

### 2.1 變數宣告與賦值

<UniversalEditor title="變數宣告與賦值對比" compare={true}>
```javascript !! js
// JavaScript - 動態型別，無需顯式宣告型別
let name = "John Doe";
const age = 25;
var oldWay = "不推薦使用";

// 變數可以動態改變型別
let x = 42;
x = "hello";  // 在 JavaScript 中是合法的
x = [1, 2, 3]; // 在 JavaScript 中是合法的

// 解構賦值
const [first, second] = [1, 2];
const { name: userName, age: userAge } = { name: "Jane", age: 30 };

console.log(name, age, x);
```

```c !! c
#include <stdio.h>

int main() {
    // C - 靜態型別，需要顯式宣告型別
    char name[] = "John Doe";
    const int age = 25;
    
    // 變數型別不能改變
    int x = 42;
    // x = "hello";  // 編譯錯誤！不能將字串賦值給 int
    // x = {1, 2, 3}; // 編譯錯誤！不能將陣列賦值給 int
    
    // C 中沒有內建的解構賦值
    int numbers[] = {1, 2};
    int first = numbers[0];
    int second = numbers[1];
    
    printf("姓名: %s, 年齡: %d, x: %d\n", name, age, x);
    printf("第一個: %d, 第二個: %d\n", first, second);
    
    return 0;
}
```
</UniversalEditor>

**核心差異對比**

| 特性 | JavaScript | C | 描述 |
|---|---|---|---|
| **型別宣告** | 動態（無需顯式型別） | 靜態（需要顯式型別） | C 語言要求顯式宣告變數型別 |
| **型別安全** | 執行時型別檢查 | 編譯時型別檢查 | C 在編譯時捕獲型別錯誤 |
| **變數命名** | camelCase | snake_case (約定) | C 社群傾向於使用 snake_case |
| **常數** | `const` | `const` 關鍵字 | 兩者都支援常數 |
| **型別轉換** | 自動/隱式 | 需要顯式強制轉換 | C 要求顯式進行型別轉換 |

### 2.2 資料型別對比

**基本資料型別**

<UniversalEditor title="基本資料型別對比" compare={true}>
```javascript !! js
// JavaScript 基本型別
let string = "text";
let number = 42;
let floatNum = 3.14;
let boolean = true;
let nullValue = null;
let undefinedValue = undefined;
let symbol = Symbol("unique");

// 引用型別
let array = [1, 2, 3];
let object = { key: "value" };
let functionName = () => {};

console.log(typeof string);        // "string"
console.log(typeof number);        // "number"
console.log(typeof floatNum);      // "number"
console.log(typeof boolean);       // "boolean"
console.log(typeof nullValue);     // "object" (JavaScript 的一個 bug)
console.log(typeof undefinedValue); // "undefined"
console.log(typeof symbol);        // "symbol"
console.log(typeof array);         // "object"
console.log(typeof object);        // "object"
console.log(typeof functionName);  // "function"
```

```c !! c
#include <stdio.h>
#include <stdbool.h>

int main() {
    // C 基本型別
    char string[] = "text";
    int number = 42;
    float float_num = 3.14f;
    double double_num = 3.14;
    bool boolean = true;  // C99 標準
    char character = 'A';
    
    // 大小資訊
    printf("char 型別大小: %zu 位元組\n", sizeof(char));
    printf("int 型別大小: %zu 位元組\n", sizeof(int));
    printf("float 型別大小: %zu 位元組\n", sizeof(float));
    printf("double 型別大小: %zu 位元組\n", sizeof(double));
    printf("bool 型別大小: %zu 位元組\n", sizeof(bool));
    
    // 型別檢查 (C 沒有像 JavaScript 那樣的執行時型別檢查)
    printf("字串: %s\n", string);
    printf("數字: %d\n", number);
    printf("浮點數: %f\n", float_num);
    printf("雙精度浮點數: %lf\n", double_num);
    printf("布林值: %s\n", boolean ? "true" : "false");
    printf("字元: %c\n", character);
    
    return 0;
}
```
</UniversalEditor>

**型別系統對比**

| 型別 | JavaScript | C | 大小 (典型) | 描述 |
|---|---|---|---|---|
| **整數** | `number` | `int` | 4 位元組 | 有號整數 |
| **浮點數** | `number` | `float` | 4 位元組 | 單精度 |
| **雙精度** | `number` | `double` | 8 位元組 | 雙精度 |
| **字元** | `string` | `char` | 1 位元組 | 單個字元 |
| **字串** | `string` | `char[]` | 可變 | 字元陣列 |
| **布林** | `boolean` | `bool` | 1 位元組 | 真/假值 |
| **空** | `null` | `NULL` | - | 空指標 |

### 2.3 控制結構

#### 2.3.1 條件陳述式

<UniversalEditor title="條件陳述式對比" compare={true}>
```javascript !! js
// JavaScript if-else
let age = 18;
let status;

if (age >= 18) {
    status = "adult";
} else if (age >= 13) {
    status = "teenager";
} else {
    status = "child";
}

// 三元運算子
let canVote = age >= 18 ? "Yes" : "No";

// Switch 陳述式
let day = 3;
let dayName;

switch (day) {
    case 1:
        dayName = "Monday";
        break;
    case 2:
        dayName = "Tuesday";
        break;
    case 3:
        dayName = "Wednesday";
        break;
    default:
        dayName = "Unknown";
}

console.log(status, canVote, dayName);
```

```c !! c
#include <stdio.h>
#include <string.h> // 需要包含 string.h 用於 strcpy

int main() {
    // C if-else
    int age = 18;
    char status[20];
    
    if (age >= 18) {
        strcpy(status, "adult");
    } else if (age >= 13) {
        strcpy(status, "teenager");
    } else {
        strcpy(status, "child");
    }
    
    // 三元運算子
    char* can_vote = (age >= 18) ? "Yes" : "No";
    
    // Switch 陳述式
    int day = 3;
    char day_name[20];
    
    switch (day) {
        case 1:
            strcpy(day_name, "Monday");
            break;
        case 2:
            strcpy(day_name, "Tuesday");
            break;
        case 3:
            strcpy(day_name, "Wednesday");
            break;
        default:
            strcpy(day_name, "Unknown");
    }
    
    printf("狀態: %s, 能否投票: %s, 星期: %s\n", status, can_vote, day_name);
    
    return 0;
}
```
</UniversalEditor>

#### 2.3.2 迴圈

<UniversalEditor title="迴圈對比" compare={true}>
```javascript !! js
// JavaScript 迴圈
let numbers = [1, 2, 3, 4, 5];

// For 迴圈
for (let i = 0; i < numbers.length; i++) {
    console.log(numbers[i]);
}

// For...of 迴圈 (ES6+)
for (let num of numbers) {
    console.log(num);
}

// For...in 迴圈 (用於物件屬性)
let person = { name: "John", age: 30 };
for (let key in person) {
    console.log(key + ": " + person[key]);
}

// While 迴圈
let i = 0;
while (i < 5) {
    console.log("While 迴圈:", i);
    i++;
}

// Do-while 迴圈
let j = 0;
do {
    console.log("Do-while 迴圈:", j);
    j++;
} while (j < 3);
```

```c !! c
#include <stdio.h>

int main() {
    int numbers[] = {1, 2, 3, 4, 5};
    int size = 5;
    
    // For 迴圈
    for (int i = 0; i < size; i++) {
        printf("%d\n", numbers[i]);
    }
    
    // C 沒有 for...of 或 for...in 迴圈
    // 你需要手動迭代
    
    // While 迴圈
    int i = 0;
    while (i < 5) {
        printf("While 迴圈: %d\n", i);
        i++;
    }
    
    // Do-while 迴圈
    int j = 0;
do {
        printf("Do-while 迴圈: %d\n", j);
        j++;
    } while (j < 3);
    
    return 0;
}
```
</UniversalEditor>

### 2.4 函數

<UniversalEditor title="函數對比" compare={true}>
```javascript !! js
// JavaScript 函數
function greet(name) {
    return "Hello, " + name + "!";
}

// 箭頭函數 (ES6+)
const add = (a, b) => a + b;

// 帶預設參數的函數
function createUser(name, age = 18, city = "Unknown") {
    return {
        name: name,
        age: age,
        city: city
    };
}

// 帶其餘參數的函數
function sum(...numbers) {
    return numbers.reduce((total, num) => total + num, 0);
}

// 函數運算式
const multiply = function(a, b) {
    return a * b;
};

console.log(greet("John"));
console.log(add(5, 3));
console.log(createUser("Alice", 25, "New York"));
console.log(sum(1, 2, 3, 4, 5));
console.log(multiply(4, 7));
```

```c !! c
#include <stdio.h>
#include <stdarg.h>
#include <string.h>

// C 函數 - 必須宣告傳回型別和參數型別
char* greet(char* name) {
    static char result[100];  // 使用 static 變數傳回字串
    sprintf(result, "Hello, %s!", name);
    return result;
}

// 固定參數的函數
int add(int a, int b) {
    return a + b;
}

// C 沒有像 JavaScript 那樣的預設參數
// 你需要手動處理預設值
typedef struct {
    char name[50];
    int age;
    char city[50];
} User;

User createUser(char* name, int age, char* city) {
    User user;
    strcpy(user.name, name);
    user.age = age;
    strcpy(user.city, city);
    return user;
}

// 可變參數 (類似於其餘參數)
int sum(int count, ...) {
    va_list args;
    va_start(args, count);
    
    int total = 0;
    for (int i = 0; i < count; i++) {
        total += va_arg(args, int);
    }
    
    va_end(args);
    return total;
}

// 函數指標 (類似於函數運算式)
int multiply(int a, int b) {
    return a * b;
}

int main() {
    printf("%s\n", greet("John"));
    printf("和: %d\n", add(5, 3));
    
    User user = createUser("Alice", 25, "New York");
    printf("使用者: %s, %d, %s\n", user.name, user.age, user.city);
    
    printf("1,2,3,4,5 的和: %d\n", sum(5, 1, 2, 3, 4, 5));
    
    // 函數指標的使用
    int (*func_ptr)(int, int) = multiply;
    printf("乘積: %d\n", func_ptr(4, 7));
    
    return 0;
}
```
</UniversalEditor>

**函數對比**

| 特性 | JavaScript | C | 描述 |
|---|---|---|---|
| **宣告** | `function` 關鍵字或箭頭函數 | 傳回型別 + 函數名 | C 要求顯式傳回型別 |
| **參數** | 動態型別 | 需要顯式型別 | C 強制型別安全 |
| **預設參數** | 內建支援 | 手動實作 | C 需要手動處理預設值 |
| **其餘參數** | `...args` | `va_list` | C 使用可變參數 |
| **傳回值** | 任何型別 | 顯式型別 | C 要求顯式傳回型別 |
| **函數運算式** | 匿名函數 | 函數指標 | C 使用函數指標 |

### 2.5 陣列和字串

<UniversalEditor title="陣列和字串對比" compare={true}>
```javascript !! js
// JavaScript 陣列
let numbers = [1, 2, 3, 4, 5];

// 陣列方法
numbers.push(6);
numbers.pop();
numbers.unshift(0);
numbers.shift();

// 陣列迭代
numbers.forEach(num => console.log(num));
let doubled = numbers.map(num => num * 2);
let evens = numbers.filter(num => num % 2 === 0);

// JavaScript 字串
let str = "Hello, World!";
let length = str.length;
let upper = str.toUpperCase();
let lower = str.toLowerCase();
let substring = str.substring(0, 5);

console.log("數字:", numbers);
console.log("翻倍:", doubled);
console.log("偶數:", evens);
console.log("字串長度:", length);
console.log("大寫:", upper);
console.log("子字串:", substring);
```

```c !! c
#include <stdio.h>
#include <string.h>
#include <ctype.h> // for toupper

int main() {
    // C 陣列 - 固定大小，沒有內建方法
    int numbers[5] = {1, 2, 3, 4, 5};
    int size = 5;
    
    // 手動陣列操作 (沒有 push/pop 方法)
    // 要新增元素，需要手動管理大小
    int new_numbers[6];
    for (int i = 0; i < size; i++) {
        new_numbers[i] = numbers[i];
    }
    new_numbers[size] = 6;  // 新增新元素
    size++;
    
    // 陣列迭代
    for (int i = 0; i < size; i++) {
        printf("%d ", new_numbers[i]);
    }
    printf("\n");
    
    // 手動陣列操作 (沒有 map/filter 方法)
    int doubled[6];
    for (int i = 0; i < size; i++) {
        doubled[i] = new_numbers[i] * 2;
    }
    
    // C 字串 - 以空字元結尾的字元陣列
    char str[] = "Hello, World!";
    int length = strlen(str);
    
    // 字串操作
    char upper[100];
    strcpy(upper, str);
    for (int i = 0; upper[i]; i++) {
        upper[i] = toupper(upper[i]); // 轉換為大寫
    }
    
    char substring[10];
    strncpy(substring, str, 5);
    substring[5] = '\0';  // 新增空字元結尾
    
    printf("數字: ");
    for (int i = 0; i < size; i++) {
        printf("%d ", new_numbers[i]);
    }
    printf("\n");
    
    printf("翻倍: ");
    for (int i = 0; i < size; i++) {
        printf("%d ", doubled[i]);
    }
    printf("\n");
    
    printf("字串長度: %d\n", length);
    printf("大寫: %s\n", upper);
    printf("子字串: %s\n", substring);
    
    return 0;
}
```
</UniversalEditor>

### 2.6 運算子和運算式

<UniversalEditor title="運算子對比" compare={true}>
```javascript !! js
// JavaScript 運算子
let a = 10, b = 3;

// 算術運算子
console.log("加法:", a + b);
console.log("減法:", a - b);
console.log("乘法:", a * b);
console.log("除法:", a / b);
console.log("取模:", a % b);
console.log("指數:", a ** b);

// 比較運算子
console.log("相等:", a == b);
console.log("嚴格相等:", a === b);
console.log("不等:", a != b);
console.log("嚴格不等:", a !== b);
console.log("大於:", a > b);
console.log("小於:", a < b);

// 邏輯運算子
console.log("與:", true && false);
console.log("或:", true || false);
console.log("非:", !true);

// 位元運算子
console.log("與:", a & b);
console.log("或:", a | b);
console.log("異或:", a ^ b);
console.log("左移:", a << 1);
console.log("右移:", a >> 1);

// 賦值運算子
let x = 5;
x += 3;  // x = x + 3
x -= 2;  // x = x - 2
x *= 4;  // x = x * 4
console.log("最終 x:", x);
```

```c !! c
#include <stdio.h>
#include <math.h>

int main() {
    int a = 10, b = 3;
    
    // 算術運算子
    printf("加法: %d\n", a + b);
    printf("減法: %d\n", a - b);
    printf("乘法: %d\n", a * b);
    printf("除法: %d\n", a / b);
    printf("取模: %d\n", a % b);
    printf("指數: %.2f\n", pow(a, b));  // 需要 math.h
    
    // 比較運算子
    printf("相等: %s\n", a == b ? "true" : "false");
    printf("不等: %s\n", a != b ? "true" : "false");
    printf("大於: %s\n", a > b ? "true" : "false");
    printf("小於: %s\n", a < b ? "true" : "false");
    printf("大於等於: %s\n", a >= b ? "true" : "false");
    printf("小於等於: %s\n", a <= b ? "true" : "false");
    
    // 邏輯運算子
    printf("與: %s\n", (1 && 0) ? "true" : "false");
    printf("或: %s\n", (1 || 0) ? "true" : "false");
    printf("非: %s\n", (!1) ? "true" : "false");
    
    // 位元運算子
    printf("與: %d\n", a & b);
    printf("或: %d\n", a | b);
    printf("異或: %d\n", a ^ b);
    printf("左移: %d\n", a << 1);
    printf("右移: %d\n", a >> 1);
    printf("非: %d\n", ~a);
    
    // 賦值運算子
    int x = 5;
x += 3;  // x = x + 3
x -= 2;  // x = x - 2
x *= 4;  // x = x * 4
    printf("最終 x: %d\n", x);
    
    return 0;
}
```
</UniversalEditor>

## 3. 核心差異總結

### 3.1 型別系統

| 方面 | JavaScript | C |
|---|---|---|
| **型別檢查** | 執行時 | 編譯時 |
| **型別宣告** | 可選 | 必需 |
| **型別轉換** | 自動 | 顯式 |
| **記憶體管理** | 自動 (GC) | 手動 |
| **空值安全** | `null`/`undefined` | `NULL` 指標 |

### 3.2 記憶體管理

| 方面 | JavaScript | C |
|---|---|---|
| **分配** | 自動 | 手動 (`malloc`) |
| **釋放** | 自動 (GC) | 手動 (`free`) |
| **記憶體安全** | 內建 | 程式設計師負責 |
| **緩衝區溢位** | 受保護 | 可能發生 |
| **記憶體洩漏** | 罕見 | 常見 |

### 3.3 錯誤處理

| 方面 | JavaScript | C |
|---|---|---|
| **例外** | Try-catch | 傳回值 |
| **錯誤型別** | 內建型別 | 自訂錯誤碼 |
| **偵錯** | 瀏覽器開發工具 | GDB, Valgrind |
| **執行時錯誤** | 自動捕獲 | 可能導致程式崩潰 |

## 4. 常見陷阱與解決方案

### 4.1 型別相關問題

<UniversalEditor title="型別安全範例" compare={true}>
```javascript !! js
// JavaScript - 靈活但可能導致 bug
let x = "5";
let y = 10;
let result = x + y;  // "510" (字串拼接)
console.log(result);

// 型別強制轉換可能很棘手
if (x == 5) {  // true (鬆散相等)
    console.log("x 等於 5");
}
if (x === 5) {  // false (嚴格相等)
    console.log("x 嚴格等於 5");
}
```

```c !! c
#include <stdio.h>
#include <stdlib.h> // for atoi

int main() {
    // C - 嚴格型別防止了許多錯誤
    char x[] = "5";
    int y = 10;
    // int result = x + y;  // 編譯錯誤！不能將字串和整數相加
    
    // 需要正確的型別轉換
    int x_num = atoi(x);  // 將字串轉換為整數
    int result = x_num + y;
    printf("結果: %d\n", result);
    
    // C 沒有像 JavaScript 那樣的鬆散相等
    if (x_num == 5) {  // 僅進行數值比較
        printf("x 等於 5\n");
    }
    
    return 0;
}
```
</UniversalEditor>

### 4.2 記憶體管理問題

<UniversalEditor title="記憶體管理範例" compare={true}>
```javascript !! js
// JavaScript - 自動記憶體管理
function createArray() {
    let arr = [];
    for (let i = 0; i < 1000000; i++) {
        arr.push(i);
    }
    return arr;
}

let data = createArray();
// 當不再被引用時，記憶體會自動釋放
data = null;  // GC 會進行清理
```

```c !! c
#include <stdio.h>
#include <stdlib.h>

int* createArray() {
    // C - 手動記憶體管理
    int* arr = malloc(1000000 * sizeof(int));
    if (arr == NULL) {
        printf("記憶體分配失敗！\n");
        return NULL;
    }
    
    for (int i = 0; i < 1000000; i++) {
        arr[i] = i;
    }
    return arr;
}

int main() {
    int* data = createArray();
    if (data != NULL) {
        // 使用資料...
        printf("第一個元素: %d\n", data[0]);
        
        // 必須手動釋放記憶體
        free(data);
        data = NULL;  // 良好的實踐，避免懸空指標
    }
    
    return 0;
}
```
</UniversalEditor>

## 5. 給 JavaScript 開發者的最佳實踐

### 5.1 學習策略

1. **從靜態型別開始**: 習慣顯式型別宣告
2. **練習記憶體管理**: 學會正確分配和釋放記憶體
3. **使用偵錯工具**: 掌握 GDB 和 Valgrind 進行偵錯
4. **編寫安全程式碼**: 始終檢查傳回值並驗證輸入
5. **理解指標**: 這是 C 編程的關鍵

### 5.2 程式碼組織

<UniversalEditor title="程式碼組織範例" compare={true}>
```javascript !! js
// JavaScript - 靈活的組織方式
class Calculator {
    constructor() {
        this.history = [];
    }
    
    add(a, b) {
        const result = a + b;
        this.history.push(`${a} + ${b} = ${result}`);
        return result;
    }
    
    getHistory() {
        return this.history;
    }
}

const calc = new Calculator();
console.log(calc.add(5, 3));
console.log(calc.getHistory());
```

```c !! c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// C - 結構化編程方法
#define MAX_HISTORY 100
#define MAX_EXPRESSION 50

typedef struct {
    char expressions[MAX_HISTORY][MAX_EXPRESSION];
    int count;
} Calculator;

// 函數宣告 (原型)
Calculator* createCalculator();
int add(Calculator* calc, int a, int b);
void printHistory(Calculator* calc);
void destroyCalculator(Calculator* calc);

int main() {
    Calculator* calc = createCalculator();
    
    printf("結果: %d\n", add(calc, 5, 3));
    printHistory(calc);
    
    destroyCalculator(calc);
    return 0;
}

// 函數實作
Calculator* createCalculator() {
    Calculator* calc = malloc(sizeof(Calculator));
    if (calc != NULL) {
        calc->count = 0;
    }
    return calc;
}

int add(Calculator* calc, int a, int b) {
    int result = a + b;
    
    if (calc != NULL && calc->count < MAX_HISTORY) {
        snprintf(calc->expressions[calc->count], MAX_EXPRESSION, 
                "%d + %d = %d", a, b, result);
        calc->count++;
    }
    
    return result;
}

void printHistory(Calculator* calc) {
    if (calc != NULL) {
        printf("歷史記錄:\n");
        for (int i = 0; i < calc->count; i++) {
            printf("%s\n", calc->expressions[i]);
        }
    }
}

void destroyCalculator(Calculator* calc) {
    if (calc != NULL) {
        free(calc);
    }
}
```
</UniversalEditor>

## 6. 下一步

掌握了語法對比之後，你將：

1. **理解 C 的靜態型別**: 領會編譯時型別檢查的優勢
2. **掌握記憶體管理**: 學會正確的記憶體分配和釋放
3. **使用指標**: 理解記憶體位址和指標操作
4. **編寫安全的 C 程式碼**: 避免常見的陷阱和記憶體錯誤
5. **使用 C 工具**: 掌握偵錯和開發工具

**下一模組**: 變數、型別和記憶體基礎 - 我們將更深入地探討 C 的記憶體模型以及變數是如何在記憶體中儲存的。

準備好探索 C 編程的奇妙世界了嗎？讓我們繼續學習基礎知識吧！

```